#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<unistd.h>
#include<sys/select.h>
#include<ctype.h>

#define SERPORT 60000
#define SERIP   "127.0.0.1"/*
	开始只有0 1 2 程序运行默认打开3个fd，这个新建的是4
*/
int socketinit()
{
  int lfd=socket(AF_INET,SOCK_STREAM,0);
  int opt=1;
  setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  struct sockaddr_in addr={
  .sin_family=AF_INET,
  .sin_port=htons(SERPORT),
  .sin_addr={
   .s_addr=INADDR_ANY
   }
  };
  bind(lfd,(struct sockaddr*)&addr,sizeof(addr));
  listen(lfd,10);
  return lfd;
}

int main()
{
	int ret=0;
	int cfd=0;
	int listenfd=socketinit();
	fd_set readset,allset;
	struct sockaddr_in addr;
	char buf[100]={0};
	int addrlen=sizeof(addr);
	
	//先将allset清空，再放入要监听的fd，当前只有lfd 1个.
	FD_ZERO(&allset);
	FD_SET(listenfd,&allset);

	int maxfd=listenfd;
	int i=0,j=0,n=0;

	while(1)
	{
		readset=allset;//每次select之前 更新readset为allset  监听所有allset的读事件
		//本次实验 只监听allset集合中的读事件 写，异常事件 不管
		ret=select(maxfd+1,&readset,NULL,NULL,NULL);
		if(ret<0)
		{
			perror("select ret < 0\n");
			break;
		}else if(ret==0)
		{
			printf("select timeout\n");
			continue;
		}else
		{
			//检查 readset 中的lfd 是否置1了，置1说明有lfd的读事件.即有客户端连接
			//只有lfd的读事件是cfd请求连接.cfd的读事件 就是cfd给ser发送数据，可以直接read读数据
			if(FD_ISSET(listenfd,&readset))
			{
				cfd=accept(listenfd,(struct sockaddr*)&addr,&addrlen);
				FD_SET(cfd,&allset);//将新连接的cfd加入监听集合 下次select它，若它发数据 就能监控到了
				if(maxfd<cfd) maxfd=cfd;//更新maxfd
				if(ret==1){ //ret==1，正好是lfd的事件 直接continue即可，因为后面肯定没事件了
					printf("ret=1,is listenfd event,has handled\n");
					continue;
				}
			}
			//has other cfd event need hand，ret肯定是>=1 的
			for(i=listenfd+1;i<=maxfd;i++)
			{
				if(FD_ISSET(i,&readset))//是当前i的客户端发来了数据
				{
					n=read(i,buf,sizeof(buf));
					if(n<0)
					{
						printf("read err\n");
					}else if(n==0)//i 客户端 断开连接了 通过read 能知道
					{
						printf("cfd=%d has disconnect\n",i);
						close(i);
						FD_CLR(i,&allset);
					}else
					{
						for(j=0;j<n;j++)
						{
							buf[j]=toupper(buf[j]);//小写字符转大写 返回给连接的客户端
						}
						write(i,buf,n);//rcv n send n
					}
				}
			}
		}
	}
}
